package org.benetech.util;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Contents of a zip file, including various ways to look at it.
 * @author Reuben Firmin
 */
public class ZipContents {

    private Map<String, File> contents;
    private File basePath;

    /**
     * Construct with a map of path/filename to file references.
     * @param contents never null
     * @param basePath the base path that the zip has been extracted to
     */
    public ZipContents(final Map<String, File> contents, final File basePath) {
        this.basePath = basePath;
        this.contents = contents;
    }

    /**
     * The path that files are located under.
     * @return never null.
     */
    public final File getBasePath() {
        return basePath;
    }

    /**
     * Array of the names of the files within this zip.
     * @return never null.
     */
    public final String[] getFileNames() {
        return contents.keySet().toArray(new String[contents.size()]);
    }

    /**
     * Array of the files within this zip.
     * @return never null.
     */
    public final File[] getFiles() {
        return contents.values().toArray(new File[contents.size()]);
    }

    /**
     * Return a file based on the file name. This method is lenient and will either accept the absolute path (location
     * that the zip was unzipped to + path) or the relative path (relative to the zip's structure). The first is more
     * efficient.
     * @param fileName never null.
     * @return null if it isn't present.
     */
    public final File getFileByName(final String fileName) {
    	final File file = contents.get(fileName);
        if (file == null) {
            return contents.get(basePath.getAbsolutePath() + File.separator + fileName);
        }
        return file;
    }

    /**
     * Whether or not the zip file contains the given file name. This method is
     * lenient, and will allow either the absolute path (i.e. including the
     * path that the contents were expanded to) or the relative path (relative
     * to the expanded base path.)  Also checks the FileName with the systems seperatorChar.
     * @param fileName never null
     * @return true if it is present, false otherwise.
     */
    public final boolean contains(final String fileName) {
        if (!(contents.containsKey(fileName)
            || contents.containsKey(basePath.getAbsolutePath() + File.separator
                    + fileName))) {
        	return contents.containsKey(fileName.replace('\\', File.separatorChar).replace('/', File.separatorChar))
                    || contents.containsKey(basePath.getAbsolutePath() + File.separator
                            + fileName.replace('\\', File.separatorChar).replace('/', File.separatorChar));
        }
           return true;
    }

    /**
     * Whether or not the zip file contains the given file name with Ignored Case. This method is
     * lenient, and will allow either the absolute path (i.e. including the
     * path that the contents were expanded to) or the relative path (relative
     * to the expanded base path.)  Also checks the FileName with the systems seperatorChar.  Not an efficient call.
     * @param fileName never null
     * @return true if it is present, false otherwise.
     */

    public final boolean containsIgnoreCase(final String fileName) {
        final Iterator<String> it = contents.keySet().iterator();
        String cur;
        while (it.hasNext()) {
        	cur = it.next();
        	if (0 == cur.compareToIgnoreCase(
        			fileName.replace('\\', File.separatorChar).replace('/', File.separatorChar))
        			|| 0 == cur.compareToIgnoreCase(basePath.getAbsolutePath() + File.separator
        					+ fileName.replace('\\', File.separatorChar).replace('/', File.separatorChar))) {
        		return true;
        	}
        }
        return false;
    }
    /**
     * Returns a list of files from the map whose names match a given regexp.
     * @param pattern e.g. "^.*\\.[xX][mM][lL]"
     * @return never null.
     */
    public final List<File> getFilesMatchingPattern(final String pattern) {
    	final List<File> fileList = new ArrayList<File>();
        for (Map.Entry<String, File> file : contents.entrySet()) {
            if (file.getKey().matches(pattern)) {
                fileList.add(file.getValue());
            }
        }
        return fileList;
    }

    /**
     * Returns a list of files from the map whose names don't match any of the
     * given patterns.
     * @param patterns patterns that the file doesn't match
     * @return Never null
     */
    public final List<File> getFilesNotMatchingPattern(final String... patterns)
    {
    	final List<File> fileList = new ArrayList<File>();
        for (Map.Entry<String, File> file : contents.entrySet()) {
            boolean match = false;
            for (String pattern : patterns) {
                if (file.getKey().matches(pattern)) {
                    match = true;
                }
            }
            if (!match) {
                fileList.add(file.getValue());
            }
        }
        return fileList;
    }

}
